package com.gangwangchina.android.contactlib.model;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import com.gangwangchina.android.contactlib.ContactsInterface;
import com.gangwangchina.android.contactlib.exceptions.NoIdException;

import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.Nickname;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Im;


public class PersonContact {

	public static final String _ID = RawContacts._ID;
	public static final String ACCOUNT_NAME = RawContacts.ACCOUNT_NAME;
	public static final String ACCOUNT_TYPE = RawContacts.ACCOUNT_TYPE;
	public static final String PERSON_ID = RawContacts.CONTACT_ID;
	public static final String CONTENT_ITEM_TYPE = RawContacts.CONTENT_ITEM_TYPE;

	public long id;

	public String accountName, accountType;
	public long personId;
	public PersonName name;
	public List<EmailAddress> emailAddresses;
	public List<TelephoneNumber> telephoneNumbers;
	public List<PostalAddress> postalAddresses;
	public List<Organization> organizations;
	public Notes notes;
	public NickName nickname;
	public List<WebSite> websites;
	public List<IMAddress> ims;
	public PersonPhoto photo;

	/**
	 * Creates a new Telephone Number that is not (yet) present in the database
	 * @param accountName
	 * @param accountType
	 * @return
	 */
	public static PersonContact newPersonContact(String accountName, String accountType){
		return new PersonContact(accountName, accountType);
	}
	
	public static PersonContact newPersonContact(String accountName){
		return new PersonContact(accountName, null);
	}
	
	/**
	 * Creates an object representation of a PersonContact that is present in the database
	 * @param personId
	 * @param accountName
	 * @param accountType
	 * @return
	 */
	public static PersonContact recoverPersonContact(long personId, String accountName, String accountType){
		return new PersonContact(personId, accountName, accountType);
	}
	
	/**
	 * Creates an object representation of a PersonContact that is present in the database
	 * @param id
	 * @param personId
	 * @param accountName
	 * @param accountType
	 * @return
	 */
	public static PersonContact recoverPersonContact(long id, long personId, String accountName, String accountType){
		PersonContact pc = recoverPersonContact(personId, accountName, accountType);
		pc.id = id;
		return pc;
	}
	
	private PersonContact(String accountName, String accountType){
		this(ContactsInterface.NO_ID, accountName, accountType);
	}
	
	/**
	 * Creates a new PersonContact
	 * 
	 * @param personId
	 *            the id of the Person that the PersonCOntact is linked to
	 * @param accountName
	 *            the account name
	 * @param accountType
	 *            the account type
	 */
	private PersonContact(long personId, String accountName, String accountType) {
		this.personId = personId;
		this.accountName = accountName;
		this.accountType = accountType;
		this.emailAddresses = new LinkedList<EmailAddress>();
		this.telephoneNumbers = new LinkedList<TelephoneNumber>();
		this.postalAddresses = new LinkedList<PostalAddress>();
		this.organizations = new LinkedList<Organization>();	
		
		this.notes = null;
		this.nickname = null;
		this.websites = new LinkedList<WebSite>();
		this.ims = new LinkedList<IMAddress>();
		this.photo = null;
	}

	/**
	 * Fills the PersonContact with DataElements from the database This method
	 * should be used only on PersonContact objects that are present in the
	 * database
	 * 
	 * @param ctx
	 *            the context in which access to the database will be made
	 * @throws NoIdException 
	 */
	public void fill(Context ctx) throws NoIdException {
		if(id==ContactsInterface.INVALID_ID||id==ContactsInterface.NO_ID) throw new NoIdException(id);
		fillName(ctx);
		fillEmails(ctx);
		fillTelephones(ctx);
		
//		 fillPostals(ctx);
		
		fillOrganization(ctx);
		fillNotes(ctx);
		fillNickName(ctx);	
		fillWebsite(ctx);
		fillIMs(ctx);
		fillPersonPhoto( ctx );
	}

	/**
	 * Sets a new Id to the current PersonContact, and links it to its
	 * DataElements This method should be used after a successfull insert in the
	 * database
	 * 
	 * @param id
	 */
	public void setNewId(long id) {
		this.id = id;
		if(this.name != null)
		{
			this.name.setNewPersonContactId(id);
		}
		
		for (Iterator<EmailAddress> it = emailAddresses.iterator(); it.hasNext();)
			it.next().setNewPersonContactId(id);
		
		for (Iterator<TelephoneNumber> it = telephoneNumbers.iterator(); it.hasNext();)
			it.next().setNewPersonContactId(id);
		
		for(Iterator<PostalAddress> it = postalAddresses.iterator(); it.hasNext();)
			it.next().setNewPersonContactId(id);
		
		for(Iterator<Organization> it = organizations.iterator(); it.hasNext();)
			it.next().setNewPersonContactId(id);
		
		if (this.notes != null)
		{
			this.notes.setNewPersonContactId(id);
		}
		
		if (this.nickname != null)
		{
			this.nickname.setNewPersonContactId(id);
		}
		
		for(Iterator<WebSite> it = websites.iterator(); it.hasNext();)
			it.next().setNewPersonContactId(id);
		
		for(Iterator<IMAddress> it = this.ims.iterator(); it.hasNext();)
			it.next().setNewPersonContactId(id);
		
		if ( this.photo != null)
		{
			this.photo.setNewPersonContactId(id);
		}
		
	}
	
	public void addTelephoneNumber(TelephoneNumber tn){
		tn.personContactId = this.id;
		telephoneNumbers.add(tn);
	}
	
	public void addEmailAddress(EmailAddress ea){
		ea.personContactId = this.id;
		emailAddresses.add(ea);
	}
	
	public void addPostalAddress(PostalAddress pa){
		pa.personContactId = this.id;
		postalAddresses.add(pa);
	}
	
	public void addOrganization(Organization org){
		org.personContactId = this.id;
		organizations.add(org);
	}

	public void addNotes(Notes notes)
	{		
		this.notes = notes;
		// this.notes.personContactId = this.id;
	}
	
	public void addWebSite(WebSite website){
		website.personContactId = this.id;
		this.websites.add(website);
	}
	
	public void addIM(IMAddress im)
	{
		im.personContactId = this.id;
		this.ims.add(im);
	}
	
	private void fillName(Context ctx) {
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,
				PersonName.DISPLAY_NAME, PersonName.GIVEN_NAME,
				PersonName.FAMILY_NAME, PersonName.PREFIX,
				PersonName.MIDDLE_NAME, PersonName.SUFFIX };
		String mimetype = PersonName.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		while (c.moveToNext()) {
			name = PersonName.recoverPersonName(c.getLong(1), c.getString(3), c.getString(4),
					c.getString(5), c.getString(6), c.getString(7));
			name.id = c.getLong(0);
		}
		c.close();
	}

	private void fillEmails(Context ctx) {
		emailAddresses = new LinkedList<EmailAddress>();
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,
				EmailAddress.EMAIL, EmailAddress.TYPE, EmailAddress.LABEL };
		String mimetype = EmailAddress.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		EmailAddress e;
		while (c.moveToNext()) {
			e = EmailAddress.recoverEmailAddress(c.getLong(1), c.getString(2), c.getInt(3), c
					.getString(4));
			e.id = c.getLong(0);
			emailAddresses.add(e);
		}
		c.close();
	}

	private void fillTelephones(Context ctx) {
		telephoneNumbers = new LinkedList<TelephoneNumber>();
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,
				TelephoneNumber.NUMBER, TelephoneNumber.TYPE,
				TelephoneNumber.LABEL };
		String mimetype = TelephoneNumber.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		TelephoneNumber t;
		while (c.moveToNext()) {
			t = TelephoneNumber.recoverTelephoneNumber(c.getLong(1), c.getString(2), c.getInt(3),
					c.getString(4));
			t.id = c.getLong(0);
			telephoneNumbers.add(t);
		}
		c.close();
	}

	private void fillPostals(Context ctx) {
		postalAddresses = new LinkedList<PostalAddress>();
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,
				PostalAddress.TYPE, PostalAddress.LABEL, PostalAddress.STREET,
				PostalAddress.POBOX, PostalAddress.NEIGHBORHOOD,
				PostalAddress.CITY, PostalAddress.REGION,
				PostalAddress.POSTCODE, PostalAddress.COUNTRY };
		String mimetype = PostalAddress.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		PostalAddress p;
		while (c.moveToNext()) {
			p = PostalAddress.recoverPostalAddress(c.getLong(1), c.getInt(2), c.getString(3), c
					.getString(4), c.getString(5), c.getString(6), c
					.getString(7), c.getString(8), c.getString(9), c
					.getString(10));
			p.id = c.getLong(0);
			postalAddresses.add(p);
		}
		c.close();
	}
	
	private void fillOrganization(Context ctx) {
		organizations = new LinkedList<Organization>();
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,
				Organization.TYPE, Organization.LABEL, Organization.ORGANIZATION, Organization.COMPANY,
				Organization.DEPARTMENT, Organization.JOB_DESCRIPTION,
				Organization.OFFICE_LOCATION, 
				Organization.TITLE,Organization.PHONETIC_NAME };
		
		String mimetype = Organization.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		Organization org;
		while (c.moveToNext()) {
			org = Organization.recoverOrganization(c.getLong(1), c.getInt(2), c.getString(3), c
					.getString(4), c.getString(5), c.getString(6), c
					.getString(7), c.getString(8), c.getString(9), c.getString(10));
			org.id = c.getLong(0);
			organizations.add(org);
		}
		c.close();
	}
	
	private void fillNotes(Context ctx)
	{
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,
				Notes.NOTE };
		String mimetype = Notes.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		while (c.moveToNext()) {
			notes = Notes.recoverNotes(c.getLong(1), c.getString(2));
			notes.id = c.getLong(0);
		}
		c.close();
	}
	
	private void fillNickName(Context ctx)
	{
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,
				NickName.NAME };
		String mimetype = Nickname.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		while (c.moveToNext()) {
			this.nickname = NickName.recoverNickname(c.getLong(1), c.getString(2));
			this.nickname.id = c.getLong(0);
		}
		c.close();
	}

	private void fillWebsite(Context ctx)
	{
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,
				WebSite.URL };
		String mimetype = WebSite.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		WebSite website;
		while (c.moveToNext()) {
			website = WebSite.recoverWebsites(c.getLong(1), c.getString(2));
			website.id = c.getLong(0);
		}
		c.close();
	}
	
	
	private void fillIMs(Context ctx)
	{
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,				
				IMAddress.DATA, IMAddress.PROTOCOL};		 
		
		String mimetype = Im.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		IMAddress im;
		while (c.moveToNext()) {
			im = IMAddress.recoverIMAddress(c.getLong(1), c.getString(2), c.getInt(3));
			im.id = c.getLong(0);
			ims.add(im);
		}
		c.close();
	}
	
	private void fillPersonPhoto(Context ctx)
	{
		String[] projection = { Data._ID, Data.RAW_CONTACT_ID,				
				PersonPhoto.PHOTO};		 
		
		String mimetype = PersonPhoto.CONTENT_ITEM_TYPE;
		Cursor c = query(ctx, projection, mimetype);
		while (c.moveToNext()) {
			photo = PersonPhoto.recoverPersonPhoto(c.getLong(1), c.getBlob(2));
			photo.id = c.getLong(0);			
		}
		c.close();
	}
	
	private Cursor query(Context ctx, String[] projection, String mimetype) {
		return ctx.getContentResolver().query(
				Data.CONTENT_URI,
				projection,
				Data.RAW_CONTACT_ID + "=?" + " AND " + Data.MIMETYPE + "='"
						+ mimetype + "'", new String[] { String.valueOf(id) },
				null);
	}

	public String toString1() {
		StringBuilder sb = new StringBuilder();
		sb.append(accountType).append('/').append(accountName).append('/')
				.append(name).append('\n');
		for (Iterator<EmailAddress> it = emailAddresses.iterator(); it
				.hasNext();) {
			sb.append(it.next()).append('/');
		}
		for (Iterator<TelephoneNumber> it = telephoneNumbers.iterator(); it
				.hasNext();) {
			sb.append(it.next()).append('/');
		}
		for(Iterator<PostalAddress> it = postalAddresses.iterator(); it.hasNext();){
			sb.append(it.next()).append('/');
		}
		sb.append('\n');
		return sb.toString();
	}	
	
//	@Override
//	public String toString() {
//		String aa = "{'id':" + this.safeJson(this.personId) + "firstname" + this.safeJson(this.name.familyName) + 
//		"lastname" + this.safeJson(this.name.givenName) + "lastname" + this.safeJson(this.name.givenName) + "}";	
//		return aa;
//	}

}
